#include "../../../include/graphics/gl/GLRenderToTexture.hpp"
#include "../../../include/tools/General.hpp"
using namespace klas;
using namespace graphics;
using namespace gl;

GLRenderToTexture::GLRenderToTexture(const math::Vector2<uint32>& dim, uint32 bpp)
{
  init(dim, bpp);
}

GLRenderToTexture::~GLRenderToTexture()
{}

void GLRenderToTexture::init(const math::Vector2<uint32>& dim, uint32 bpp)
{
  // Init variables
  m_dim = dim;

  // Generate a new texture
  glEnable(GL_TEXTURE_2D);
  
  // Create the texture
  glGenTextures(1, &m_textureID);
  glBindTexture(GL_TEXTURE_2D, m_textureID);

  // Set texture data
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, dim.x, dim.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

  // Disable texturing
  glBindTexture(GL_TEXTURE_2D, 0);
  glDisable(GL_TEXTURE_2D);

  // Generate the framebuffer and bind it
  glGenFramebuffers(1, &m_fboID);
  glBindFramebuffer(GL_FRAMEBUFFER, m_fboID);

  // Attach a texture to the fbo
  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_textureID, 0);

  // Make sure the framebuffer is "complete" (check for errors)
  GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
  if(status != GL_FRAMEBUFFER_COMPLETE)
    throw error::Exception(string("GLRenderToTexture::init(..) was unable to successfully create the framebuffer with error ") + tools::General::toString(glGetError()));

  // Unbind the FBO
  glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

void GLRenderToTexture::kill()
{}

void GLRenderToTexture::enable()
{
  glBindFramebuffer(GL_FRAMEBUFFER, m_fboID);

  // Adjust the size of the viewport
  glPushAttrib(GL_VIEWPORT_BIT);
  glViewport(0, 0, m_dim.x, m_dim.y);
}

void GLRenderToTexture::disable()
{
  // Disable the viewport size change
  glPopAttrib();
  glBindFramebuffer(GL_FRAMEBUFFER, 0);
}


void GLRenderToTexture::render(const math::Vector2d& numSides, decimal alpha)
{
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, m_textureID);

  glColor4f(1.0f, 1.0f, 1.0f, alpha);
  glBegin(GL_QUADS);

  for(int x = 1; x <= numSides.x; ++x)
    for(int y = 1; y <= numSides.y; ++y)
    {

      math::Vector2<float> endTex( float( (1.0f) * (float(x) / float(numSides.x))),
                                   float( (1.0f) * (float(y) / float(numSides.y))) ),

                           startTex( float( (1.0f) * (float(x) / float(numSides.x))) - float( (1.0f) * (1.0f / float(numSides.x))),
                                     float( (1.0f) * (float(y) / float(numSides.y))) - float( (1.0f) * (1.0f / float(numSides.y))));



      math::Vector2<float> endPos( float( (m_dim.x) * (float(x) / float(numSides.x))),
                                   float( (m_dim.y) * (float(y) / float(numSides.y))) ),

                           startPos( float( (m_dim.x) * (float(x) / float(numSides.x))) - float( (m_dim.x) * (1.0f / float(numSides.x))),
                                     float( (m_dim.y) * (float(y) / float(numSides.y))) - float( (m_dim.y) * (1.0f / float(numSides.y))));




      /* MaxAmount * (currVal / maximumVal) */
   
      // start replaces 0, end replaces 1

      glTexCoord2f(startTex.x, endTex.y);
      glVertex2f(startPos.x, endPos.y);

      glTexCoord2f(endTex.x, endTex.y);
      glVertex2f(endPos.x, endPos.y);

      glTexCoord2f(endTex.x, startTex.y);
      glVertex2f(endPos.x, startPos.y);

      glTexCoord2f(startTex.x, startTex.y);
      glVertex2f(startPos.x, startPos.y);
    }
  glEnd();

  glDisable(GL_TEXTURE_2D);

}